import { isArray } from "@/utils/is";
import { FieldNamesProps } from "@/components/ProTable/interface";

const mode = import.meta.env.VITE_ROUTER_MODE;

/**
 * @description 获取localStorage
 * @param {String} key Storage名称
 * @returns {String}
 */
export function localGet(key: string) {
  const value = window.localStorage.getItem(key);
  try {
    return JSON.parse(window.localStorage.getItem(key) as string);
  } catch (error) {
    return value;
  }
}

/**
 * @description 存储localStorage
 * @param {String} key Storage名称
 * @param {*} value Storage值
 * @returns {void}
 */
export function localSet(key: string, value: any) {
  window.localStorage.setItem(key, JSON.stringify(value));
}

/**
 * @description 清除localStorage
 * @param {String} key Storage名称
 * @returns {void}
 */
export function localRemove(key: string) {
  window.localStorage.removeItem(key);
}

/**
 * @description 清除所有localStorage
 * @returns {void}
 */
export function localClear() {
  window.localStorage.clear();
}

/**
 * @description 判断数据类型
 * @param {*} val 需要判断类型的数据
 * @returns {String}
 */
export function isType(val: any) {
  if (val === null) return "null";
  if (typeof val !== "object") return typeof val;
  else return Object.prototype.toString.call(val).slice(8, -1).toLocaleLowerCase();
}

export function isWeekday(date: Date): boolean {
  const day = date.getUTCDay();
  return day !== 0 && day !== 6; // 0=周日, 6=周六
}

/**
 * 计算两个日期相差天数
 * @param startDate 开始日期
 * @param endDate 结束日期
 * @returns 天数
 */
export function dateDiffInWeekdays(startDate: Date | string, endDate: Date | string): number {
  // 确保参数是 Date 对象
  const start = startDate instanceof Date ? startDate : new Date(startDate);
  const end = endDate instanceof Date ? endDate : new Date(endDate);

  // 检查日期是否有效
  if (isNaN(start.getTime()) || isNaN(end.getTime())) {
    return 0;
  }

  let count = 0;
  const tempStartDate = new Date(start.getFullYear(), start.getMonth(), start.getDate());
  const tempEndDate = new Date(end.getFullYear(), end.getMonth(), end.getDate());
  if (tempStartDate <= tempEndDate) {
    while (tempStartDate < tempEndDate) {
      count++;
      tempStartDate.setDate(tempStartDate.getDate() + 1);
    }
  } else {
    while (tempStartDate > tempEndDate) {
      count--;
      tempStartDate.setDate(tempStartDate.getDate() - 1);
    }
  }

  return count;
}

/**
 * @description 生成唯一 uuid
 * @returns {String}
 */
export function generateUUID() {
  let uuid = "";
  for (let i = 0; i < 32; i++) {
    let random = (Math.random() * 16) | 0;
    if (i === 8 || i === 12 || i === 16 || i === 20) uuid += "-";
    uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
  }
  return uuid;
}

/**
 * 判断两个对象是否相同
 * @param {Object} a 要比较的对象一
 * @param {Object} b 要比较的对象二
 * @returns {Boolean} 相同返回 true，反之 false
 */
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
  if (!a || !b) return false;
  let aProps = Object.getOwnPropertyNames(a);
  let bProps = Object.getOwnPropertyNames(b);
  if (aProps.length != bProps.length) return false;
  for (let i = 0; i < aProps.length; i++) {
    let propName = aProps[i];
    let propA = a[propName];
    let propB = b[propName];
    if (!b.hasOwnProperty(propName)) return false;
    if (propA instanceof Object) {
      if (!isObjectValueEqual(propA, propB)) return false;
    } else if (propA !== propB) {
      return false;
    }
  }
  return true;
}

/**
 * @description 生成随机数
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @returns {Number}
 */
export function randomNum(min: number, max: number): number {
  let num = Math.floor(Math.random() * (min - max) + max);
  return num;
}

/**
 * @description 获取当前时间对应的提示语
 * @returns {String}
 */
export function getTimeState() {
  let timeNow = new Date();
  let hours = timeNow.getHours();
  if (hours >= 6 && hours <= 10) return `早上好 ⛅`;
  if (hours >= 10 && hours <= 14) return `中午好 🌞`;
  if (hours >= 14 && hours <= 18) return `下午好 🌞`;
  if (hours >= 18 && hours <= 24) return `晚上好 🌛`;
  if (hours >= 0 && hours <= 6) return `凌晨好 🌛`;
}

/**
 * @description 获取浏览器默认语言
 * @returns {String}
 */
export function getBrowserLang() {
  let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
  let defaultBrowserLang = "";
  if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) {
    defaultBrowserLang = "zh";
  } else {
    defaultBrowserLang = "en";
  }
  return defaultBrowserLang;
}

/**
 * @description 获取不同路由模式所对应的 url + params
 * @returns {String}
 */
export function getUrlWithParams() {
  const url = {
    hash: location.hash.substring(1),
    history: location.pathname + location.search
  };
  return url[mode];
}

/**
 * @description 使用递归扁平化菜单，方便添加动态路由
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 */
export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] {
  let newMenuList = JSON.parse(JSON.stringify(menuList));
  return newMenuList.flatMap(item => {
    if (!item._id) {
      item._id = generateUUID();
    }
    item.children?.forEach(child => {
      if (!child._id) {
        child._id = generateUUID();
      }
      child.pid = item._id;
    });
    return [item, ...(item.children && item.children.length > 0 ? getFlatMenuList(item.children) : [])];
  });
}

/**
 * @description 使用递归过滤出需要渲染在左侧菜单的列表 (需剔除 isHide == true 的菜单)
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 * */
export function getShowMenuList(menuList: Menu.MenuOptions[]) {
  let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
  return newMenuList.filter(item => {
    item.children?.length && (item.children = getShowMenuList(item.children));
    return !item.meta?.isHide;
  });
}

/**
 * @description 使用递归找出所有面包屑存储到 pinia/vuex 中
 * @param {Array} menuList 菜单列表
 * @param {Array} parent 父级菜单
 * @param {Object} result 处理后的结果
 * @returns {Object}
 */
export const getAllBreadcrumbList = (menuList: Menu.MenuOptions[], parent = [], result: { [key: string]: any } = {}) => {
  for (const item of menuList) {
    result[item.path] = [...parent, item];
    if (item.children) getAllBreadcrumbList(item.children, result[item.path], result);
  }
  return result;
};

/**
 * @description 使用递归处理路由菜单 path，生成一维数组 (第一版本地路由鉴权会用到，该函数暂未使用)
 * @param {Array} menuList 所有菜单列表
 * @param {Array} menuPathArr 菜单地址的一维数组 ['**','**']
 * @returns {Array}
 */
export function getMenuListPath(menuList: Menu.MenuOptions[], menuPathArr: string[] = []): string[] {
  for (const item of menuList) {
    if (typeof item === "object" && item.path) menuPathArr.push(item.path);
    if (item.children?.length) getMenuListPath(item.children, menuPathArr);
  }
  return menuPathArr;
}

/**
 * @description 递归查询当前 path 所对应的菜单对象 (该函数暂未使用)
 * @param {Array} menuList 菜单列表
 * @param {String} path 当前访问地址
 * @returns {Object | null}
 */
export function findMenuByPath(menuList: Menu.MenuOptions[], path: string): Menu.MenuOptions | null {
  for (const item of menuList) {
    if (item.path === path) return item;
    if (item.children) {
      const res = findMenuByPath(item.children, path);
      if (res) return res;
    }
  }
  return null;
}

/**
 * @description 使用递归过滤需要缓存的菜单 name (该函数暂未使用)
 * @param {Array} menuList 所有菜单列表
 * @param {Array} keepAliveNameArr 缓存的菜单 name ['**','**']
 * @returns {Array}
 * */
export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveNameArr: string[] = []) {
  menuList.forEach(item => {
    item.meta.isKeepAlive && item.name && keepAliveNameArr.push(item.name);
    item.children?.length && getKeepAliveRouterName(item.children, keepAliveNameArr);
  });
  return keepAliveNameArr;
}

/**
 * @description 格式化表格单元格默认值 (el-table-column)
 * @param {Number} row 行
 * @param {Number} col 列
 * @param {*} callValue 当前单元格值
 * @returns {String}
 * */
export function formatTableColumn(row: number, col: number, callValue: any) {
  // 如果当前值为数组，使用 / 拼接（根据需求自定义）
  if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "--";
  return callValue ?? "--";
}

/**
 * @description 处理 ProTable 值为数组 || 无数据
 * @param {*} callValue 需要处理的值
 * @returns {String}
 * */
export function formatValue(callValue: any) {
  // 如果当前值为数组，使用 / 拼接（根据需求自定义）
  if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "--";
  return callValue ?? "--";
}

/**
 * @description 处理 prop 为多级嵌套的情况，返回的数据 (列如: prop: user.name)
 * @param {Object} row 当前行数据
 * @param {String} prop 当前 prop
 * @returns {*}
 * */
export function handleRowAccordingToProp(row: { [key: string]: any }, prop: string) {
  if (!prop.includes(".")) return row[prop] ?? "--";
  prop.split(".").forEach(item => (row = row[item] ?? "--"));
  return row;
}

/**
 * @description 处理 prop，当 prop 为多级嵌套时 ==> 返回最后一级 prop
 * @param {String} prop 当前 prop
 * @returns {String}
 * */
export function handleProp(prop: string) {
  const propArr = prop.split(".");
  if (propArr.length == 1) return prop;
  return propArr[propArr.length - 1];
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 label 和 value 的 key值，会自动识别格式化）
 * @param {String} cellValue 当前单元格值
 * @param {Array} enumData 字典列表
 * @param {Array} fieldNames label && value && children 的 key 值
 * @param {String} type 过滤类型（目前只有 tag）
 * @returns {String}
 * */
export function filterEnum(cellValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") {
  const value = fieldNames?.value ?? "value";
  const label = fieldNames?.label ?? "label";
  const children = fieldNames?.children ?? "children";
  let filterData: { [key: string]: any } = {};
  if (Array.isArray(enumData)) {
    filterData = findItemNested(enumData, cellValue, value, children);
  }
  // 判断是否输出的结果为 tag 类型
  if (type == "tag") {
    return filterData?.tagType ? filterData.tagType : "";
  } else {
    return filterData ? filterData[label] : "--";
  }
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 label 和 value 的 key值，会自动识别格式化）
 * @param {String} cellValue 当前单元格值
 * @param {Array} enumData 字典列表
 * @param {Array} fieldNames label && value && children 的 key 值
 * @param {String} type 过滤类型（目前只有 tag）
 * @returns {String}
 * */
export function filterEnums(cellValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") {
  const value = fieldNames?.value ?? "value";
  const label = fieldNames?.label ?? "label";
  const children = fieldNames?.children ?? "children";
  let filterData: { [key: string]: any } = {};
  if (Array.isArray(enumData)) {
    filterData = findItemNested(enumData, cellValue, value, children);
  }
  // 判断是否输出的结果为 tag 类型
  if (type == "tag") {
    return filterData?.tagType ? filterData.tagType : "";
  } else {
    return filterData ? filterData[label] : "--";
  }
}

/**
 * @description 递归查找 callValue 对应的 enum 值
 * */
export function findItemNested(enumData: any, callValue: any, value: string, children: string) {
  return enumData.reduce((accumulator: any, current: any) => {
    if (accumulator) return accumulator;
    if (current[value] === callValue) {
      return current;
    }
    if (current[children]) return findItemNested(current[children], callValue, value, children);
  }, null);
}

/**
 * 上级数组.
 * @param treeData 数据
 * @param id ID
 * @returns 级联数组
 */
export function getCascadeValue(treeData: any, id: string, idPropName = "_id", childrenPropName = "children") {
  let result = <any>[];
  let data = findFromTree(treeData, id, idPropName, childrenPropName);
  if (data) {
    result.push(data._id);
    while (data && data.pid) {
      data = findFromTree(treeData, data.pid, idPropName, childrenPropName);
      if (data && data._id) {
        result.push(data._id);
      }
    }
  }
  return result.reverse();
}

export const findFromTree = (treeArray: any, id: string, idPropName = "_id", childrenPropName = "children") => {
  if (!treeArray || treeArray == null || treeArray.length <= 0) return null;
  for (let i = 0; i < treeArray.length; i++) {
    if (treeArray[i][idPropName] === id) {
      return treeArray[i];
    } else {
      let result = findFromTree(treeArray[i][childrenPropName], id, idPropName, childrenPropName);
      if (result != null) {
        return result;
      }
    }
  }
  return null;
};

/**
 * 对象深度复制
 * @param {*对象数组} source
 */
export function deepObjCopy(source) {
  let sourceCopy = source instanceof Array ? [] : {};
  for (let item in source) {
    // typeof null 会返回 "object"，防止有属性为null时被复制为[object, object]
    if (source[item] == null) {
      sourceCopy[item] = source[item];
    } else {
      sourceCopy[item] = typeof source[item] === "object" ? deepObjCopy(source[item]) : source[item];
    }
  }
  return sourceCopy;
}

/**
 * 复制对象属性
 * @param {Object} target 目标对象
 * @param {Object} source 源对象
 * @param {Object} options 选项
 * @param {boolean} options.deep 是否深拷贝，默认为 true
 * @param {boolean} options.merge 是否合并模式，默认为 true
 * @param {boolean} options.overwrite 是否覆盖已存在的值，默认为 true
 * @returns {Object} 处理后的目标对象
 */
export function copyProperties(target, source, options: any = {}) {
  const { deep = true, merge = true, overwrite = true } = options;

  if (!source || typeof source !== "object") return target;
  if (!target || typeof target !== "object") target = {};

  Reflect.ownKeys(source).forEach(key => {
    const sourceValue = source[key];
    const targetValue = target[key];

    // 如果不允许覆盖且目标值已存在，则跳过
    if (!overwrite && targetValue !== undefined) {
      return;
    }

    // 如果是深拷贝且值是对象类型
    if (deep && sourceValue && typeof sourceValue === "object") {
      // 处理数组
      if (Array.isArray(sourceValue)) {
        target[key] =
          merge && Array.isArray(targetValue)
            ? [...targetValue, ...sourceValue.map(item => (item && typeof item === "object" ? deepClone(item) : item))]
            : sourceValue.map(item => (item && typeof item === "object" ? deepClone(item) : item));
      }
      // 处理日期对象
      else if (sourceValue instanceof Date) {
        target[key] = new Date(sourceValue);
      }
      // 处理正则表达式
      else if (sourceValue instanceof RegExp) {
        target[key] = new RegExp(sourceValue.source, sourceValue.flags);
      }
      // 处理普通对象
      else {
        target[key] =
          merge && targetValue && typeof targetValue === "object"
            ? copyProperties(targetValue, sourceValue, options)
            : copyProperties({}, sourceValue, options);
      }
    }
    // 如果是浅拷贝或原始类型值
    else {
      // 根据merge和overwrite选项决定是否设置值
      if (merge || targetValue === undefined) {
        target[key] = sourceValue;
      }
    }
  });

  return target;
}

// 辅助函数：完整的深克隆
function deepClone(obj) {
  if (!obj || typeof obj !== "object") return obj;

  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item));
  }

  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);

  return copyProperties({}, obj, true);
}

/**
 * 数组格式转树状结构
 * @param   {array}     array
 * @param   {String}    id
 * @param   {String}    pid
 * @param   {String}    children
 * @return  {Array}
 */
export function arrayToTree(array, tree = { id: "_id", pid: "pid", children: "children" }) {
  array = JSON.parse(JSON.stringify(array));
  let result: any[] = [];
  let map = {};
  array.forEach(item => {
    map[item[tree.id]] = item;
  });

  array.forEach(item => {
    let parent = map[item[tree.pid]];
    if (parent) {
      !parent[tree.children] && (parent[tree.children] = []);
      parent[tree.children].push(item);
    } else {
      result.push(item);
    }
  });
  return result;
}

/**
 * 对比两个对象的差异
 * @param {Object} obj1 第一个对象
 * @param {Object} obj2 第二个对象
 * @param {String} path 当前属性路径
 * @returns {Array} 差异数组
 */
export function compareObjects(obj1, obj2, path = "") {
  const differences: any[] = [];

  // 如果两个值都是 null 或 undefined，认为它们相等
  if (obj1 == null && obj2 == null) {
    return differences;
  }

  // 如果其中一个值是 null 或 undefined
  if (obj1 == null || obj2 == null) {
    differences.push({
      path: path || "root",
      value1: obj1,
      value2: obj2
    });
    return differences;
  }

  // 获取所有键
  const allKeys = [...new Set([...Object.keys(obj1), ...Object.keys(obj2)])];

  allKeys.forEach(key => {
    const value1 = obj1[key];
    const value2 = obj2[key];
    const currentPath = path ? `${path}.${key}` : key;

    // 如果两个值类型不同
    if (typeof value1 !== typeof value2) {
      differences.push({
        path: currentPath,
        value1,
        value2
      });
      return;
    }

    // 如果是对象类型，递归比较
    if (typeof value1 === "object" && value1 !== null) {
      // 处理数组
      if (Array.isArray(value1) || Array.isArray(value2)) {
        if (!Array.isArray(value1) || !Array.isArray(value2) || value1.length !== value2.length) {
          differences.push({
            path: currentPath,
            value1,
            value2
          });
        } else {
          // 比较数组的每个元素
          value1.forEach((item, index) => {
            const itemDiffs = compareObjects(item, value2[index], `${currentPath}[${index}]`);
            differences.push(...itemDiffs);
          });
        }
      }
      // 处理日期对象
      else if (value1 instanceof Date || value2 instanceof Date) {
        if (!(value1 instanceof Date) || !(value2 instanceof Date) || value1.getTime() !== value2.getTime()) {
          differences.push({
            path: currentPath,
            value1,
            value2
          });
        }
      }
      // 处理普通对象
      else {
        const nestedDiffs = compareObjects(value1, value2, currentPath);
        differences.push(...nestedDiffs);
      }
    }
    // 如果是基本类型，直接比较
    else if (value1 !== value2) {
      differences.push({
        path: currentPath,
        value1,
        value2
      });
    }
  });

  return differences;
}

/**
 * 计算加班时间（小时）
 * 排除每日 12:00-13:30 和 17:30-18:30 的时间
 * 支持任意时间段（包括周末、凌晨、跨天）
 *
 * @param {string|Date} startTime - 开始时间（ISO字符串 或 Date 对象）
 * @param {string|Date} endTime   - 结束时间（ISO字符串 或 Date 对象）
 * @returns {number} 加班总小时数，保留两位小数
 */
export function calculateOvertimeHours(startTime: undefined | Date, endTime: undefined | Date) {
  if (!startTime || !endTime) {
    return 0;
  }

  const start = startTime instanceof Date ? startTime : new Date(startTime);
  const end = endTime instanceof Date ? endTime : new Date(endTime);

  if (isNaN(start.getTime()) || isNaN(end.getTime()) || end <= start) {
    return 0;
  }

  // 获取本地时间的当日分钟数（0 ~ 1440）
  const getLocalMinutes = date => {
    return date.getHours() * 60 + date.getMinutes() + date.getSeconds() / 60;
  };

  // 克隆日期（时间归零）
  const resetTime = date => {
    const d = new Date(date);
    d.setHours(0, 0, 0, 0);
    return d;
  };

  const excludeRanges = [
    { start: 12 * 60, end: 13.5 * 60 }, // 12:00 - 13:30 → [720, 810)
    { start: 17.5 * 60, end: 18.5 * 60 } // 17:30 - 18:30 → [1050, 1110)
  ];

  let totalMinutes = 0;
  let current = new Date(start);

  while (current < end) {
    const dayStart = resetTime(current);
    const nextDay = new Date(dayStart);
    nextDay.setDate(nextDay.getDate() + 1);

    // 当前处理的时间段：[segStart, segEnd)
    const segStart = current < start ? start : current;
    const segEnd = nextDay < end ? nextDay : end;

    // 转为当天的分钟偏移（segEnd 可能跨天，需处理）
    let segStartMin = getLocalMinutes(segStart);
    let segEndMin = getLocalMinutes(segEnd);

    // 如果 segEnd 是次日 00:00，则 segEndMin = 0，但应视为 1440
    if (segEnd.getDate() !== segStart.getDate()) {
      segEndMin += 24 * 60; // +1440
    }

    // 初始有效分钟数 = 整个时间段长度
    let validMinutes = segEndMin - segStartMin;

    // 减去与每个排除区间的重叠部分
    for (const range of excludeRanges) {
      // 排除区间在当天的范围（不跨天）
      const exclStart = range.start;
      const exclEnd = range.end;

      // 计算 [segStartMin, segEndMin) 与 [exclStart, exclEnd) 的交集
      const overlapStart = Math.max(segStartMin, exclStart);
      const overlapEnd = Math.min(segEndMin, exclEnd);

      if (overlapEnd > overlapStart) {
        validMinutes -= overlapEnd - overlapStart;
      }
    }

    totalMinutes += Math.max(0, validMinutes);
    current = nextDay;
  }

  const hours = totalMinutes / 60;
  return Math.round(hours * 100) / 100;
}

export function isPasswordStrong(password: string | undefined) {
  if (!password) return false;
  const lowerCaseLetters = /[a-z]/g;
  const upperCaseLetters = /[A-Z]/g;
  const numbers = /[0-9]/g;
  const specialChars = /[\W_]/g; // \W 匹配任何非单词字符。等价于 [^a-zA-Z0-9_]

  return (
    password.match(lowerCaseLetters) &&
    password.match(upperCaseLetters) &&
    password.match(numbers) &&
    password.match(specialChars) &&
    password.length > 8
  );
}
